This vignette demonstrates how to calibrate phenological parameters
for a grapevine variety and site using the
phenologyCalibration() function.
We’ll use sample datasets included in the package:
colliOrientali: daily weather databbchSample: observed BBCH stagesphenomenalsParameters: model parametersA data frame containing hourly or daily weather data. The function supports flexible column names (aliases are automatically detected).
| Column | Aliases (recognized) | Units | Time step | Mandatory? |
|---|---|---|---|---|
Site |
site, station, location | – | hourly/daily | Yes |
DateTime / Date |
date, datetime, timestamp | Date (POSIXct or chr mm-dd-yyyy) |
hourly/daily | Yes |
Hour |
hour, hr | 0–23 | hourly only | Yes |
Temperature |
temp, temperature, t2m | °C | hourly only | Yes |
Tmax |
tmax, maxtemp, t2mmax | °C | daily | Yes |
Tmin |
tmin, mintemp, t2mmin | °C | daily | Yes |
Precipitation |
prec, rainfall, rain, prectotcorr | mm | hourly/daily | Yes |
RelativeHumidity |
rh, humidity, relhumidity | % | hourly | Optional |
RelativeHumidityMax |
rhmax, humiditymax, relhumiditymax, relativehumiditymax, hummax | % | daily | Optional |
RelativeHumidityMin |
rhmin, humiditymin, relhumiditymin, relativehumiditymin, hummin | % | daily | Optional |
WindSpeed |
wind, ws | m/s | hourly/daily | Optional |
Radiation |
rad, solar, solarrad | MJ/m² (daily and hourly) | hourly/daily | Optional |
Latitude |
latitude, lat | decimal deg | hourly/daily | Yes |
Notes:
For daily data, only Tmax, Tmin, Precipitation and Latitude are strictly required.
For hourly data, only Temperature, Precipitation, Hour, DateTime and Latitude are required.
If RelativeHumidity or Radiation are missing, they are automatically estimated using Tmax, Tmin, precipitation, and latitude.
A data frame containing BBCH phenological observations. This dataset must include at least the following columns:
| Column | Description | Example |
|---|---|---|
Variety |
Variety name | CabernetS |
Site |
Site name (must match sites in weather_data) |
ColliOrientali |
Latitude |
Latitude of the site (decimal degrees) | 44.0 |
Longitude |
Longitude of the site (decimal degrees) | 11.0 |
Date |
Date of observation (format: yyyy-mm-dd) |
2007-04-02 |
BBCH |
BBCH growth stage (numeric code, see BBCH scale) | 8, 65 |
A nested list of model parameters. This list is typically loaded from
phenomenalsParameters
and can be customized.
Structure:
list[
species][[class]][[parameter]] = list(
min = <minimum calibration value>,
max = <maximum calibration value>,
value = <default value>,
calibration = <TRUE/FALSE>
)Notes:
Parameters marked with calibration = TRUE are optimized during calibration.
The list is automatically exported to the correct CSV format for the C# engine; you do not need to handle file conversions.
| Argument | Description | Default |
|---|---|---|
start_year |
First year of the calibration period | 2000 |
end_year |
Last year of the calibration period | 2025 |
sites |
Site names (must match Site in
weather_data and referenceBBCH). Use
"all" to include all sites. |
"all" |
varieties |
Varieties to include (must match Variety in
referenceBBCH). Use "all" to include all
varieties. |
"all" |
iterations |
Number of iterations for the simplex algorithm | 100 |
timestep |
Time step of weather_data (“daily” or
“hourly”) |
"daily" |
| Site | Date | Tmax | Tmin | Precipitation | WindSpeed | RelativeHumidityMax | RelativeHumidityMin | Radiation | Latitude | Longitude |
|---|---|---|---|---|---|---|---|---|---|---|
| ColliOrientali | 1/1/2004 | 10.00 | 3.88 | 0 | 4.606482 | 78 | 53 | 4.954 | 45.64986 | 12.44986 |
| ColliOrientali | 1/2/2004 | 7.01 | 2.64 | 0 | 5.162037 | 63 | 49 | 3.285 | 45.64986 | 12.44986 |
| ColliOrientali | 1/3/2004 | 5.55 | -0.71 | 0 | 6.516204 | 49 | 33 | 5.702 | 45.64986 | 12.44986 |
| ColliOrientali | 1/4/2004 | 3.65 | -4.57 | 0 | 6.203704 | 55 | 32 | 6.393 | 45.64986 | 12.44986 |
| ColliOrientali | 1/5/2004 | 3.37 | -5.42 | 0 | 4.050926 | 62 | 28 | 6.082 | 45.64986 | 12.44986 |
| ColliOrientali | 1/6/2004 | 3.21 | -3.16 | 0 | 7.187500 | 60 | 39 | 5.486 | 45.64986 | 12.44986 |
| Variety | Site | Latitude | Longitude | Date | BBCH |
|---|---|---|---|---|---|
| Carmenere | ColliOrientali | 44 | 11 | 2007-04-01 | 8 |
| Carmenere | ColliOrientali | 44 | 11 | 2008-04-28 | 8 |
| Carmenere | ColliOrientali | 44 | 11 | 2009-04-19 | 8 |
| Carmenere | ColliOrientali | 44 | 11 | 2010-04-21 | 8 |
| Carmenere | ColliOrientali | 44 | 11 | 2011-04-18 | 8 |
| Carmenere | ColliOrientali | 44 | 11 | 2012-04-26 | 8 |
calib_result <- phenologyCalibration(
weather_data = colliOrientali,
referenceBBCH = bbchSample,
phenomenalsParameters =phenomenalsParameters,
start_year = 2006,
end_year = 2012,
sites = "ColliOrientali",
varieties = "Carmenere",
iterations = 100,
timestep = "daily"
)
#> 🌦️ Weather data written for site 'ColliOrientali' ➜ ColliOrientali.csv
#> 🐢 Initiating phenology calibration...
#> ⌛ Be cool — algorithms are thinking really hard right now.
#> 🍷 Good wine takes time, and so does this model!
#>
#> calibration running on Carmenere, ColliOrientali....
#> RMSE (days): 29.6 | █ RMSE (days): 13.5 | █ RMSE (days): 16733.3 | █ RMSE (days): 11.1 | █ RMSE (days): 12.6 | █ RMSE (days): 32.7 | █ RMSE (days): 21.0 | █ RMSE (days): 16733.2 | █ RMSE (days): 24.5 | █ RMSE (days): 24.7 | █ RMSE (days): 6324.7 | █ RMSE (days): 6324.7 | █ RMSE (days): 29.5 | █ RMSE (days): 27.9 | █ RMSE (days): 26.6 | █ RMSE (days): 23.2 | █ RMSE (days): 10.2 | █ RMSE (days): 11.2 | █ RMSE (days): 17.9 | █ RMSE (days): 14.5 | █ RMSE (days): 14.7 | █ RMSE (days): 13.6 | █ RMSE (days): 14.5 | █ RMSE (days): 12.8 | █ RMSE (days): 16.7 | █ RMSE (days): 9.8 | █ RMSE (days): 9.1 | █ RMSE (days): 17.9 | █ RMSE (days): 8.6 | █ RMSE (days): 15.1 | █ RMSE (days): 9.3 | █ RMSE (days): 8.7 | █ RMSE (days): 9.9 | █ RMSE (days): 8.8 | ██ RMSE (days): 8.8 | ██ RMSE (days): 10.5 | ██ RMSE (days): 8.1 | ██ RMSE (days): 10.8 | ██ RMSE (days): 7.9 | ██ RMSE (days): 8.6 | ██ RMSE (days): 7.8 | ██ RMSE (days): 11.9 | ██ RMSE (days): 7.8 | ██ RMSE (days): 11.3 | ██ RMSE (days): 7.6 | ██ RMSE (days): 8.6 | ██ RMSE (days): 11.3 | ██ RMSE (days): 7.4 | ██ RMSE (days): 7.3 | ██ RMSE (days): 8.9 | ██ RMSE (days): 9.1 | ██ RMSE (days): 7.4 | ██ RMSE (days): 9.8 | ██ RMSE (days): 7.6 | ██ RMSE (days): 9.8 | ██ RMSE (days): 7.3 | ██ RMSE (days): 8.5 | ██ RMSE (days): 7.4 | ██ RMSE (days): 7.5 | ██ RMSE (days): 9.0 | ██ RMSE (days): 7.0 | ██ RMSE (days): 8.1 | ██ RMSE (days): 7.2 | ██ RMSE (days): 7.2 | ██ RMSE (days): 7.1 | ██ RMSE (days): 7.1 | ██ RMSE (days): 8.2 | ███ RMSE (days): 6.9 | ███ RMSE (days): 7.4 | ███ RMSE (days): 7.0 | ███ RMSE (days): 6.9
#> | ███ RMSE (days): 7.4 | ███ RMSE (days): 7.2 | ███ RMSE (days): 7.0 | ███ RMSE (days): 8.7 | ███ RMSE (days): 6.7 | ███ RMSE (days): 6.8 | ███ RMSE (days): 7.2 | ███ RMSE (days): 7.1 | ███ RMSE (days): 7.4 | ███ RMSE (days): 6.8 | ███ RMSE (days): 6.7 | ███ RMSE (days): 7.3 | ███ RMSE (days): 6.8 | ███ RMSE (days): 6.9 | ███ RMSE (days): 6.5 | ███ RMSE (days): 6.7 | ███ RMSE (days): 7.1 | ███ RMSE (days): 6.9 | ███ RMSE (days): 6.6 | ███ RMSE (days): 6.7 | ███ RMSE (days): 6.8 | ███ RMSE (days): 7.1 | ███ RMSE (days): 6.7 | ███ RMSE (days): 6.7 | ███ RMSE (days): 6.8 | ███ RMSE (days): 6.7 | ███ RMSE (days): 6.6 | ███ RMSE (days): 6.8 | ███ RMSE (days): 6.6 | ████ RMSE (days): 7.0 | ████ RMSE (days): 6.4 | ████ RMSE (days): 6.7 | ████ RMSE (days): 6.4 | ████ RMSE (days): 6.8 | ████ RMSE (days): 6.5 | ████ RMSE (days): 6.8 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.6 | ████ RMSE (days): 7.1 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.9 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.5 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.5 | ████ RMSE (days): 6.8 | ████ RMSE (days): 6.4 | ████ RMSE (days): 6.7 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.5 | ████ RMSE (days): 6.4 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.7 | ████ RMSE (days): 6.6 | ████ RMSE (days): 6.4 | ████ RMSE (days): 6.5 | ████ RMSE (days): 6.5 | ████ RMSE (days): 6.5 | ████ RMSE (days): 6.4 | ████ RMSE (days): 6.6 | █████ RMSE (days): 6.5 | █████ RMSE (days): 6.6 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.6 | █████ RMSE (days):
#> 6.5 | █████ RMSE (days): 6.6 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.8 | █████ RMSE (days): 6.3 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.6 | █████ RMSE (days): 6.5 | █████ RMSE (days): 6.5 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.5 | █████ RMSE (days): 6.6 | █████ RMSE (days): 6.6 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.6 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.6 | █████ RMSE (days): 6.5 | █████ RMSE (days): 6.3 | █████ RMSE (days): 6.3 | █████ RMSE (days): 6.6 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.5 | █████ RMSE (days): 6.5 | █████ RMSE (days): 6.6 | █████ RMSE (days): 6.4 | █████ RMSE (days): 6.4 | ██████ RMSE (days): 6.6 | ██████ RMSE (days): 6.5 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.5 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.5 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.5 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.4 | ██████ RMSE (days): 6.5 | ██████ RMSE (days): 6.5 | ██████ RMSE (days): 6.4 | ██████
#> ✅ Calibration completed successfully
#> 🧹 Cleaning up temporary output files...
#> 🗑️ Deleted 1 temporary output files.This will:
str(calib_result, max.level = 1)
#> List of 2
#> $ parameters:'data.frame': 190 obs. of 6 variables:
#> $ phenology :'data.frame': 2557 obs. of 13 variables:This helps users see:
filtered_params <- calib_result$parameters |>
dplyr::filter(site == "colliorientali", variety == "carmenere")
knitr::kable(filtered_params, caption = "Calibrated parameters for Carmenere at ColliOrientali")| site | variety | parameter | value | min | max |
|---|---|---|---|---|---|
| colliorientali | carmenere | bbch08 | 19.201279 | 2 | 30 |
| colliorientali | carmenere | bbch55 | 54.348247 | 41 | 75 |
| colliorientali | carmenere | bbch63 | 80.731794 | 50 | 99 |
| colliorientali | carmenere | bbch65 | 39.548608 | 30 | 60 |
| colliorientali | carmenere | bbch68 | 23.806074 | 1 | 40 |
| colliorientali | carmenere | bbch81 | 55.027237 | 40 | 80 |
| colliorientali | carmenere | bbch85 | 73.477635 | 70 | 80 |
| colliorientali | carmenere | chillThreshold | 7.541195 | 6 | 9 |
| colliorientali | carmenere | chillingRequirement | 144.287297 | 80 | 300 |
| colliorientali | carmenere | cycleLength | 2157.582957 | 2000 | 2800 |
All calibrated parameters are within their defined bounds. 🎯
However, if any parameter is very close to its minimum or maximum limit, it’s good practice to slightly expand the calibration range and re-run the process. This helps avoid boundary effects and ensures the optimization isn’t artificially constrained.
📝 Tip: Parameters like
bbch65orbbch85that consistently hit the edge of their range may indicate that your biological assumptions need refining, or that more calibration iterations are needed.
This is where you show the time series of predicted BBCH stages over time.
p<-ggplot(calib_result$phenology |>
mutate(Date=as.Date(Date,format='%m/%d/%Y'),
year=year(Date)),aes(x=Date)) +
geom_line(aes(y=BBCHPhase))+
geom_point(aes(y=BBCHRef))+
theme_classic()+
geom_line(aes(y=ChillState),col='blue4')
ggplotly(p)The phenologyCalibration() function successfully fits
thermal and stress-based parameters to reproduce observed BBCH stages.
This calibrated output can now be passed to
runPhenomenals() for trait modeling.